"""
Very simple GUI app for reading/editing a single SVT file.

Usage:
  python svtgui.py 0.SVT [-edit params]
 

Options:
	-nopet
		Pet properties will be not be read.
		
	-nogold
		Gold properties will be not be read.
	
	-readOnly
		The program will open in read-only mode; no editing will be allowed.
	
	-log logfile.txt
		Creates a log file and redirects standard output to it.

	-errlog errlogfile.txt
		Creates an error log file and redirects error output to it.

	-silent
		Nothing will be printed to the console, and no log files will be 
		created (even if -out is 'console').

	-builtins builtins.cfg
		Allows you to pass a custom set of "built-in" values for pet_type and class name, overriding the
		hard-coded defaults. The file format is similar to Python's Config file format. A line starting with
		hash (`#`) or semi-colon (`;`) characters, ignoring trailing whitespace, is considered a comment and
		will be ignored. The pet_types section is denoted with `[PET_TYPES]`, and the class names section is
		denoted with `[CLASSES]`. The pet_types should be `key:value` pairs, where the key is the UNIT_GUID of
		the unit, and the value is the `DISPLAYNAME` of the unit. Separate each class name with a new line character
		(`\n`). An example file might look like this:
			
			[PET_TYPES]
			6655120081984033246:Cat
			-3195029593784249890:Dog
			1343088339509383646:Imaginary
			[CLASSES]
			Alchemist
			Vanquisher
			Destroyer
			Nether Mage
			
		Use the getbuiltins script to generate file based on masterresourceunits.dat.
		
Editing:
	-edit (since v.1.1)
		If -edit is specified, only the properties listed after it will be
		editable. This will override -readOnly.
 
Drag an SVT file onto the EXE to open the tool.
"""

import wx
import svtio
import sys
import os
from svtutil import odict, arg, indexof

ONLINE_HELP_URL = 'http://code.google.com/p/torchtools/wiki/PythonTorchtools'

class InputPanel(wx.Panel):
	isChoice = None
	props = None
	input = None
	key = None
	name = None
	value = None
	log = None
	dataType = None
	mainClass = None
	
	#parent, props, key, dataType, value, 
				#log, noEdit=False, readOnly=False
	
	def __init__(self, parent, mainClass, detail):
		wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
		
		self.mainClass = mainClass
		self.name = detail['name']
		self.dataType = detail['data']
		self.key = detail['key']
		self.props = self.mainClass.file.props
		self.log = self.mainClass.log
		self.value = self.props[self.key]
		readOnly = self.mainClass.noEdit
		
		noEdit = False
		if self.mainClass.noEdit:
			noEdit = True
		edt = self.mainClass.editables
		if len(edt)!=0 and self.key not in edt:
			noEdit = True
		
		if noEdit:
			self.dataType = svtio.DATA_STRING
		
		sizer = wx.BoxSizer(wx.HORIZONTAL)
		
		if self.dataType in mainClass.DATA_CHOICES:
			self.isChoice = True
			l = mainClass.DATA_CHOICES[self.dataType]
			self.input = wx.ComboBox(self, wx.ID_ANY, 
							value=self.value, choices=l)
			self.input.SetEditable(self.mainClass.advEdit)
		else:
			self.input = wx.TextCtrl(self, wx.ID_ANY, str(self.value))
			self.input.SetEditable(not noEdit)
		#only disable if we aren't in readOnly mode
		if not readOnly:
			if noEdit:
				self.input.Disable()
		
		sizer = wx.BoxSizer(wx.HORIZONTAL)
		sizer.Add(self.input, 5, wx.ALL, 0)
		
		self.SetSizer(sizer)
	
	def onchange(self):
		val = self.getval()
		if val is None:
			val = self.value
			t = (self.key, self.input.GetLabel(), self.dataType)
			self.log("Invalid %s value '%s' for data type %s, reverting to default" % t)
			self.input.SetLabel(str(val))
		self.value = val
		self.props[self.key] = self.value
	
	def getval(self):
		""" Returns None if there was a problem. """
		val = self.input.GetLabel()
		if self.isChoice or self.dataType==svtio.DATA_STRING:
			return val
		else:
			try:
				max = svtio.MAX_VALUES[self.dataType]
				i = int(val)
				return i if i<=max else None
			except:
				return None

class InputChoicebook(wx.Choicebook):
	mainClass = None
	pages = []
	
	def __init__(self, parent, mainClass):
		wx.Choicebook.__init__(self, parent, wx.ID_ANY)
		self.mainClass = mainClass
		
		for detail in mainClass.details:
			panel = InputPanel(self, self.mainClass, detail)
			self.AddPage(panel, panel.name)
			self.pages.append(panel)
				
		self.Bind(wx.EVT_CHOICEBOOK_PAGE_CHANGED, self.OnPageChanged)
		self.Bind(wx.EVT_CHOICEBOOK_PAGE_CHANGING, self.OnPageChanging)
 
	def onupdate(self):
		self.pages[self.GetSelection()].onchange()
 
	def OnPageChanged(self, event):
		old = event.GetOldSelection()
		self.pages[old].onchange()
		#self.mainClass.log('OnPageChanged,  old:%d, new:%d, sel:%d\n' % (old, new, sel))
		event.Skip()
 
	def OnPageChanging(self, event):
		old = event.GetOldSelection()
		#self.mainClass.log('OnPageChanging, old:%d, new:%d, sel:%d\n' % (old, new, sel))
		event.Skip()
 

class MainFrame(wx.Frame):	
	mainClass = None
	orig = {}
	
	def __init__(self, mainClass, title=""):
		self.mainClass = mainClass
		wx.Frame.__init__(self, None, wx.ID_ANY,
						  title,
						  size=(250,155))
		panel = wx.Panel(self)
		self.Bind(wx.EVT_CLOSE, self.onQuit)
		
		self.SetMinSize(self.GetSize())
		self.SetMaxSize((-1, self.GetSize().height))
		
		self.orig = self.copyProps(self.mainClass.file.props, self.orig)
		
		self.notebook = InputChoicebook(panel, mainClass)
		
		editing = not self.mainClass.noEdit
		
		btnPanel = wx.Panel(panel)
		btnSizer = wx.BoxSizer(wx.HORIZONTAL)
		if editing:
			updateBtn = wx.Button(btnPanel, wx.ID_ANY, "Update File")
			updateBtn.Bind(wx.EVT_BUTTON, self.updateFile)
			btnSizer.Add(updateBtn, 0, wx.ALL, 3)
		
		saveBtn = wx.Button(btnPanel, wx.ID_ANY, "Save && Quit" if editing else "Quit")
		saveBtn.Bind(wx.EVT_BUTTON, self.saveAndQuit if editing else self.onQuit)
		
		btnSizer.Add(saveBtn, 0, wx.ALL, 3)
		btnPanel.SetSizer(btnSizer)
		
		sizer = wx.BoxSizer(wx.VERTICAL)
		sizer.Add(self.notebook, 1, wx.ALL|wx.EXPAND, 5)
		sizer.Add(btnPanel, 0, wx.ALL|wx.ALIGN_CENTER, 5)
		panel.SetSizer(sizer)
		
		menubar = wx.MenuBar()
		file = wx.Menu()
		file.Append(1, 'Online Help')
		file.Append(2, 'About')
		menubar.Append(file, '&Help')
		self.SetMenuBar(menubar)
		menubar.Show()
		
		self.Bind(wx.EVT_MENU, self.onHelp, id=1)
		self.Bind(wx.EVT_MENU, self.onAbout, id=2)
		
		self.Layout()
		self.Center()
		self.Show()
	
	def copyProps(self, props, dest):
		for k, v in props.items():
			dest[k] = v
		return dest
	
	def onHelp(self, ev=None):
		import webbrowser
		webbrowser.open(ONLINE_HELP_URL)
	
	def onAbout(self, ev=None):
		ed = '' if self.mainClass.noEdit else ' and editing'
		wx.MessageBox('For viewing%s SVT files. Created by mattdesl.' % ed, 'About')
		
	def updateFile(self, ev=None):
		self.notebook.onupdate()
		file = self.mainClass.file
		
		#only write values that were changed
		edited = []
		for k, v in file.props.items():
			if file.props[k]==self.orig[k]:
				file.props[k] = None
			else:
				edited.append(k)
		
		self.mainClass.log('Updating properties: '+str(edited))
		
		file.text2values(file.props)
		file.update(file.props)
		file.values2text(file.props)
		self.orig = self.copyProps(file.props, self.orig)
		
	def onQuit(self, ev=None):		
		self.mainClass.closeFile()
		wx.GetApp().Exit() 
	
	def saveAndQuit(self, ev=None):
		self.updateFile()
		self.onQuit()
	
class MainClass(object):
	
	version = None
	options = {}
	appname = None
	path = None
	file = None
	details = None
	noEdit = False
	closed = False
	editables = []
	advEdit = False
	
	def __init__(self, version, args):
		self.appname = os.path.basename(args.pop(0))
		
		edInd = indexof(args, '-edit')
		if edInd!=-1:
			self.editables = args[edInd+1:]
			args = args[:edInd]
		
		#logging info...
		self.options['silent'] = self.silent = '-silent' in args
		self.options['log'] = l = arg(args, '-log')
		self.options['errlog'] = el = arg(args, '-errlog')
		self.options['readOnly'] = self.noEdit = '-readOnly' in args and len(self.editables)==0
		self.options['advEdit'] = self.advEdit = '-advEdit' in args
		self.options['builtins'] = arg(args, '-builtins')
		
		if self.options['builtins'] is not None:
			d = odict()
			svtio.loadCustomBuiltins(self.options['builtins'], petDict=d)
		
		__pettypes = svtio.PET_TYPES.values()
		__pettypes.remove('Unknown')

		#choice-type data
		self.DATA_CHOICES = {
			svtio.DATA_DIFFICULTY:svtio.DIFFICULTIES,
			svtio.DATA_CLASS:svtio.CLASSES,
			svtio.DATA_PET_TYPE:__pettypes,
			svtio.DATA_BOOLEAN:['true', 'false']
		}
		
		#if it's not silent
		if not self.options['silent']:
			if l is not None:
				sys.stdout = open(l, 'w') if not self.silent else None
			if el is not None:
				sys.stderr = open(el, 'w') if not self.silent else None
		
		self.options['linesep'] = '-linesep' in args
		self.ln = os.linesep if self.options['linesep'] else '\n'
		
		self.path = args.pop(0) if len(args)!=0 else ''
		if len(self.path)==0:
			self.log('No path specified, format is: svtgui.exe file [options]')
			raise Exception('No path specified, format is: svtgui.exe file [options]')
		self.options['nopet'] = '-nopet' in args
		self.options['nogold'] = '-nogold' in args
		
		basePath = os.path.basename(self.path)
		
		self.log('Opening file %s' % basePath)
		self.file = svtio.TLPlayerSave(self.path, 'r+b')
		self.file.nopet = self.options['nopet']
		self.file.nogold = self.options['nogold']
		self.file.update()
		self.file.values2text(self.file.props)
		self.details = self.file.details
		
		if self.noEdit:
			self.closeFile()
		
		app = wx.PySimpleApp()
		ttl = basePath
		if self.noEdit:
			ttl += ' (Read-Only)'
		frame = MainFrame(self, title=ttl)
		app.MainLoop()
	
	def closeFile(self):
		if not self.closed:
			self.log('Closing file stream')
			self.file.close()
			self.closed = True
	
	def log(self, s=''):
		if not self.silent:
			print s 
	
if __name__ == "__main__":
	version = 'v.07'
	MainClass(version, sys.argv)